var KeFuClient = (function () {
	var defaults = {
		id: 'kacnKF',
		chatWinShowAnimate: 'slideInUp',
		chatWinHideAnimate: 'slideOutDown',
		getUserInfoUrl: '/easy-chat/php/kefu.php?act=getUserInfo',
		emotionSourceUrl: '/easy-chat/js/src/Emotion.json',
		imgUploadUrl: '/easy-chat/php/fileupload.php',
		swfUrl: '/easy-chat/js/lib/webuploader/Uploader.swf'
	};

	var soundSrc = {
		online: 'asset/sound/online.mp3',
		newMessage: 'asset/sound/newmessage.mp3'
	};

	function Module(options) {
		this.options = options ? $.extend(defaults, options) : defaults;
		this.socketModule = null;
		this.socket = null;
		this.user = new User();
		this.ui = new KeFuClientUI();
		this.sound = new Sound();
		this.serviceId = null; // 客服人员ID
		this.serviceList = null; //之前对话过的客服列表
		this.talkId = null;

		this.emotionArr = null;

		this.inputingTimer = null; // 输入的定时器

		var dtdList = {};
		dtdList.register = {};
		dtdList.register.$dtd = $.Deferred();

		this.dtdList = dtdList; // deferred对象列表

		this.timerList = {}; // 定时器列表
		this.init();
	}

	// 初始化客服聊天应用，首先获取用户的基本信息
	// 当获取信息成功后，触发聊天界面
	Module.prototype.init = function () {
		this.getUserInfo();
	};

	// 连接客服系统，将当前用户发送至后台
	Module.prototype.connectSocket = function () {
		var me = this;
		me.socketModule = new KeFuClientSocket('/kefu');
		me.socket = me.socketModule.handle;
		me.listen();

		me.dtdList.register.promise = me.register(me.dtdList.register.$dtd);
	};

	// 连接客服系统，将当前用户发送至后台
	Module.prototype.disconnectSocket = function () {
		var me = this;
		me.socket.disconnect();
		me.socketModule = null;
		me.socket = null;
		me.serviceId = null;
		me.talkId = null;
	};

	// 将个人信息发送给服务器登记，服务器会根据后台设置，推送相关内容。
	// 如果是自动方式，系统会按照工号排序，以及最后一次通话时间，分配一个客服到后台
	// 如果是手动方式，系统会将部门，以及部门在线人数状态发送过来，然后访客手动选择相应的客服
	// 发送'register'后，客户端将监听到'inform'，做为服务器端的回应
	Module.prototype.register = function (dtd) {
		var me = this,
			uo = me.user.getMyInfo();
		me.socket.emit('register', uo);
		me.timerList.register = setTimeout(function () {
			alert('连接服务器超时，请重试');
		}, 5000);
		return dtd.promise();
	};

	Module.prototype.listen = function () {
		var me = this;
		me.socket.on('registerResponded', function (ro) {
			// 服务器返回分配方式，以及相应的数据
			if (ro) {
				me.informSuccess();
				if (ro && ro.hasOwnProperty('allocation_type')) {
					if (ro.allocation_type == 0) {
						if (ro.serviceId) {
							me.serviceId = ro.serviceId;
						} else {
							me.serviceId = null;
						}
						if (ro.talkId) {
							me.talkId = ro.talkId;
						} else {
							me.talkId = null;
							console.log('获取talkId错误');
						}
						if (ro.sysMessage) {
							me.showSysMessage(ro.sysMessage);
						}

						if (ro.welcome) {
							me.ui.setMessage(ro.welcome, me.user.uid);
						}
					} else if (ro.allocation_type == 1) {
						me.ui.showManualList(ro);
					}
				} else {
					console.log('不该这样的');
				}
			}
		});

		me.socket.on('connectGroupResponded', function (ro) {
			me.ui.clearMessageList();
			if (ro.serviceId) {
				me.serviceId = ro.serviceId;
			} else {
				me.serviceId = null;
			}
			if (ro.talkId) {
				me.talkId = ro.talkId;
			} else {
				me.talkId = null;
				console.log('获取talkId错误');
			}
			if (ro.sysMessage) {
				me.showSysMessage(ro.sysMessage);
			}
			if (ro.welcome) {
				me.ui.setMessage(ro.welcome, me.user.uid);
			}

			me.sound.play(soundSrc.online);
		});

		me.socket.on('gradeResponed', function (result) {
			if (result === 'gradeSuccess') {
				me.closeChatWin();
				me.resetGrade();
			}
		});

		me.socket.on('controller', function (jo) {
			if (jo && jo.action && me[jo.action]) {
				me[jo.action].call(me, jo);
			}
		});

	};

	// 服务器端成功返回请求信息
	Module.prototype.informSuccess = function () {
		var me = this;
		me.dtdList.register.$dtd.resolve(); //标记事件成功
		clearTimeout(me.timerList.register);
	};

	/**
	 * 服务端返回消息结构
	 * {
	 *     "id": "8dbae067-2e00-4220-a9f6-2de6657f440d",
	 *     "uid": 1,
	 *     "from": 1,
	 *     "to": "351523b8e6eb36ae5115205886f36f86",
	 *     "username": "test",
	 *     "message": "",
	 *     "time": "07-06 9:15:16",
	 *     "role": 9
	 * }
	 */
	Module.prototype.getMessage = function (mo) {
		var me = this;
		if (mo.id && me.user.join_id) {
			me.sound.play(soundSrc.newMessage);
			me.ui.setMessage(mo, me.user.join_id);
		}
	};

	Module.prototype.getUserInfo = function () {
		var me = this;
		me.user.getUserInfo(me.options.getUserInfoUrl, function (resp) {
			var uo = {};
			uo.uid = resp.user_id;
			uo.username = resp.username;
			uo.access_time = resp.access_time;
			uo.ip = resp.ip;
			uo.role = resp.role;
			uo.join_id = resp.join_id;
			uo.status = 1;
			me.user.setUserInfo(uo);
			me.createTriggerView();
			me.sound.init();
		});
	};

	// 创建触发加载聊天窗口的界面，并且与服务器Socket建立连接
	Module.prototype.createTriggerView = function () {
		var me = this,
			tpl = '',
			elmId = this.options.id,
			$widget;

		$widget = me.ui.$widget = $('#' + elmId);
		me.ui.$msgInput = $widget.find('.msgInput');
		me.ui.$msgList = $widget.find('.msgList');

		me.ui.bindConnectGroup(function (groupId) {
			var mo = {};
			mo.uid = me.user.uid;
			mo.join_id = me.user.join_id;
			mo.username = me.user.username;
			mo.ip = me.user.ip;
			mo.group_id = groupId;
			mo.allocation_type = 1; // 分配方式，1为手动
			mo.status = me.user.status;
			me.socket.emit('connectGroup', mo);
		});

		// 访问页面时，只在第一次点击时对页面进行渲染
		if ($widget.data('render') != 1) {

			$widget.on('click', '.js-connectCS', function () {
				if (me.ui.$layer === null) {
					me.ui.$layer = $(this).siblings('.layer');
				}
				me.ui.$layer.addClass('loading');
				//if(me.socket === null) {
				me.connectSocket();
				//}
				$widget.data('render', 1);

				me.initGrade();
				//setTimeout(function () {

				$.when(me.dtdList.register.promise).done(function () {

					$widget.addClass('kfChatting');
					var $kfcSec = $widget.find('.kfcbSec'),
						aniClass = 'animated ' + me.options.chatWinShowAnimate;
					$kfcSec.addClass(aniClass).one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function () {
						$(this).removeClass(aniClass);
					});
					me.ui.$layer.removeClass('loading');

					me.bindEnterKey();
					me.bindCloseChatWinBtn();
					me.bindEmotion();
					me.bindUploadImg();
				});

				//}, 500);
			});
		}
	};

	Module.prototype.bindCloseChatWinBtn = function () {
		var me = this,
			$widget = me.ui.$widget;
		$widget.find('.js-closeWin').on('click', function () {
			if (me.serviceId === null || $widget.attr('isGrade') == 1) {
				me.closeChatWin();
			} else {
				var $gradeLayer = $widget.find('.gradeLayer');
				$gradeLayer.show();
				$widget.attr('isGrade', 1);
			}
		});
	};

	Module.prototype.closeChatWin = function () {
		var me = this,
			$widget = me.ui.$widget,
			$kfcSec = $widget.find('.kfcbSec'),
			aniClass = 'animated ' + me.options.chatWinHideAnimate;
		var $gradeLayer = $widget.find('.gradeLayer');
		$kfcSec.addClass(aniClass).one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function () {
			$(this).removeClass(aniClass);
			$widget.removeClass('kfChatting');
			me.ui.clearMessageList();
		});
		me.disconnectSocket();
		$widget.attr('isGrade', 0);
		$gradeLayer.hide();
	};

	Module.prototype.buildMessageObj = function (msg) {
		var me = this,
			mo = {};
		mo.id = generateUUID();
		mo.talk_id = me.talkId;
		mo.from = me.user.join_id;
		mo.to = me.serviceId;
		mo.username = me.user.username;
		mo.message = msg;
		mo.time = friendlyTime();
		mo.role = me.user.role;
		return mo;
	}

	// 发送信息
	Module.prototype.sendMessage = function () {
		var me = this;
		if (me.serviceId) {
			var msg = me.ui.getMessage(me.emotionArr);
			if (msg != '') {
				var mo = me.buildMessageObj(msg);
				me.ui.setMessage(mo, me.user.join_id);
				clearTimeout(me.inputingTimer);
				me.socketModule.sendMessage(mo);
			} else {
				me.showTip(me.ui.$msgInput[0], '消息内容不能为空！', 'warn');
			}
		} else {
			me.showTip(me.ui.$widget.find('.groupUL')[0], '请先选择客服分组！', 'warn');
		}
	};

	// 显示系统消息
	Module.prototype.showSysMessage = function (msg) {
		var me = this;
		var mo = {};
		mo.id = generateUUID();
		mo.title = '<i class="sysTipIcon"></i>【系统提示】';
		mo.message = msg;
		mo.time = friendlyTime();

		me.ui.setSysMessage(mo);
	};

	// 正在输入
	Module.prototype.inputing = function () {
		var me = this,
			now = (new Date()).getTime();
		var mo = {};
		mo.id = 'p' + me.user.join_id;
		mo.from = me.user.join_id;
		mo.to = me.serviceId;
		mo.username = me.user.username;
		mo.message = me.ui.getMessage();
		mo.time = friendlyTime();
		mo.timeStamp = now;
		mo.role = me.user.role;
		me.socket.emit('inputing', mo);
	};

	Module.prototype.bindEnterKey = function () {
		var me = this,
			$textarea = $('.js-bindEnterKey');
		if ($textarea.data('isBind') != 1) {
			var mimics = [
				'paddingTop',
				'paddingRight',
				'paddingBottom',
				'paddingLeft',
				'fontSize',
				'lineHeight',
				'fontFamily',
				'width',
				'fontWeight',
				'border-top-width',
				'border-right-width',
				'border-bottom-width',
				'border-left-width',
				'borderTopStyle',
				'borderTopColor',
				'borderRightStyle',
				'borderRightColor',
				'borderBottomStyle',
				'borderBottomColor',
				'borderLeftStyle',
				'borderLeftColor'
			];

			var $twin = $('<div />').css({
					'position': 'absolute',
					'display': 'none',
					'word-wrap': 'break-word',
					'white-space': 'pre-wrap'
				}),
				lineHeight = parseInt($textarea.css('line-height'), 10) || parseInt($textarea.css('font-size'), '10'),
				minheight = parseInt($textarea.css('height'), 10) || lineHeight * 3,
				maxheight = parseInt($textarea.css('max-height'), 10) || Number.MAX_VALUE;

			var curatedWidth = Math.floor(parseInt($textarea.width(), 10));

			$twin.css({'width': curatedWidth + 'px'});

			// Append the twin to the DOM
			// We are going to meassure the height of this, not the textarea.
			$twin.appendTo($textarea.parent());
			// Copy the essential styles (mimics) from the textarea to the twin
			var i = mimics.length;
			while (i--) {
				$twin.css(mimics[i].toString(), $textarea.css(mimics[i].toString()));
			}

			// Sets a given height and overflow state on the textarea
			function setHeightAndOverflow(height, overflow) {
				var curratedHeight = Math.floor(parseInt(height, 10));
				if ($textarea.height() !== curratedHeight) {
					$textarea.css({'height': curratedHeight + 'px', 'overflow': overflow});
					var mh = me.ui.$widget.find('.pbPad').height() - me.ui.$widget.find('.msgInputBox').height();
					me.ui.$msgList.height(mh);
				}
			}

			// This function will update the height of the textarea if necessary
			function update(forced) {
				// Get curated content from the textarea.
				var textareaContent = $textarea.val().replace(/&/g, '&amp;').replace(/ {2}/g, '&nbsp;').replace(/<|>/g, '&gt;').replace(/\n/g, '<br />');
				// Compare curated content with curated twin.
				var twinContent = $twin.html().replace(/<br>/ig, '<br />');
				if (forced || textareaContent + '&nbsp;' !== twinContent) {
					// Add an extra white space so new rows are added when you are at the end of a row.
					$twin.html(textareaContent + '&nbsp;');
					// Change textarea height if twin plus the height of one line differs more than 3 pixel from textarea height
					if (Math.abs($twin.height() - $textarea.height()) > 3) {
						var goalheight = $twin.height();
						if (goalheight >= maxheight) {
							setHeightAndOverflow(maxheight, 'auto');
						} else if (goalheight <= minheight) {
							setHeightAndOverflow(minheight, 'hidden');
						} else {
							setHeightAndOverflow(goalheight, 'hidden');
						}
					}
				}
			}

			// Hide scrollbars
			$textarea.css({'overflow': 'hidden'});
			// Update textarea size on keyup, change, cut and paste
			$textarea.bind('change cut paste', function (evt) {
				update();
			});

			$textarea.on('keydown', function (evt) {
				evt = evt || window.event;
				if (evt && evt.keyCode == 13) {
					me.sendMessage();
					update();
					evt.preventDefault();
					return false;
				}
			});

			$textarea.on('keyup', function (evt) {
				clearTimeout(me.inputingTimer);
				evt = evt || window.event;
				if (evt) {
					if (evt.keyCode != 13) {
						update();
						me.inputingTimer = setTimeout(function () {
							me.inputing();
						}, 600);
					} else {
						evt.preventDefault();
						return false;
					}
				}
			});

			$textarea.bind('update', update);

			// Compact textarea on blur
			$textarea.bind('blur', function () {
				if ($twin.height() < maxheight) {
					if ($twin.height() > minheight) {
						$textarea.height($twin.height());
					} else {
						$textarea.height(minheight);
					}
				}
			});
			// And this line is to catch the browser paste event
			$textarea.bind('input paste', function (e) {
				setTimeout(update, 250);
			});
			// Run update once when elastic is initialized
			update();
			$textarea.data('isBind', 1);
		}
	};

	Module.prototype.bindEmotion = function () {
		var me = this,
			$emoBtn = me.ui.$widget.find('.js-emotion');
		if ($emoBtn.attr('bind') != 1) {
			$emoBtn.emotion({
				target: me.ui.$msgInput,
				dataSource: me.options.emotionSourceUrl,
				fn: function (resp) {
					me.emotionArr = resp;
				}
			});
			$emoBtn.attr('bind', 1);
		}
	};

	Module.prototype.bindUploadImg = function () {
		var me = this;
		new Uploader('.js-uploadImg', me.options.imgUploadUrl, me.options.swfUrl, function ($li, file) {
			var msgObj = me.buildMessageObj('loading...'),
				msgId = msgObj.id;
			me.ui.setMessage(msgObj, me.user.join_id);
			var $mCon = $('#k' + msgId).find('.mContent');
			$mCon.html($li);
			file.msg = msgObj;
		}, function (file, resp) {
			var mo = file.msg;
			mo.message = '<a href="' + resp.result + '" target="_blank"><img src="' + resp.result + '"></a>';
			me.socketModule.sendMessage(mo);
		});
	};

	// 初始化评分
	Module.prototype.initGrade = function () {
		var me = this,
			$widget = me.ui.$widget,
			$oStar = $widget.find('.gradeStar'),
			oStar = $oStar[0],
			i,
			iScore,
			iStar,
			aLi = oStar.getElementsByTagName("li"),
			oUl = oStar.getElementsByTagName("ul")[0],
			$gRes = $('.gradeResult'),
			$gtBox = $('.gradeTipBox'),
			$gTip = $('.gradeTip'),
			aMsg = [
				"很不满意|服务态度恶劣，非常不满",
				"不满意|服务态度较差",
				"一般|服务态度一般",
				"满意|服务态度较好，还是挺满意的",
				"非常满意|服务态度真挚热情，非常满意"
			];
		i = iScore = iStar = 0;
		//评分处理
		var fnPoint = function (iArg) {
			//分数赋值
			iScore = iArg || iStar;
			for (i = 0; i < aLi.length; i++) aLi[i].className = i < iScore ? "on" : "";
		};

		for (i = 1; i <= aLi.length; i++) {
			aLi[i - 1].index = i;
			//鼠标移过显示分数
			aLi[i - 1].onmouseover = function () {
				fnPoint(this.index);
				//浮动层显示
				$gtBox.show();
				//计算浮动层位置
				$gtBox.css('left', oUl.offsetLeft + this.index * this.offsetWidth - 104 + "px");
				//匹配浮动层文字内容
				$gTip.html("<em><b>" + this.index + "</b> 分 " + aMsg[this.index - 1].match(/(.+)\|/)[1] + "</em>" + aMsg[this.index - 1].match(/\|(.+)/)[1]);
			};
			//鼠标离开后恢复上次评分
			aLi[i - 1].onmouseout = function () {
				fnPoint();
				//关闭浮动层
				$gtBox.hide();
			};
			//点击后进行评分处理
			aLi[i - 1].onclick = function () {
				iStar = this.index;
				$gtBox.hide();
				$gRes.html("<strong>" + (iStar) + " 分</strong> (" + aMsg[iStar - 1].match(/\|(.+)/)[1] + ")");
				$('#gradeScore').val(iStar);
			};
		}

		$widget.find('.js-closeGrade').on('click', function () {
			$widget.find('.gradeLayer').hide();
		});

		$widget.find('.js-submitGrade').on('click', function () {
			var $this = $(this);
			var data = $('#gradeForm').serializeArray();
			if ($.isArray(data)) {
				var obj = {};
				obj.talkId = me.talkId;
				$.each(data, function (k, val) {
					obj[val.name] = val.value;
				});
				me.socket.emit('grade', obj);
				$this.attr('disabled', 'disabled').addClass('disabled');
			}
		});
	};

	// 重置打分
	Module.prototype.resetGrade = function () {
		var me = this,
			$widget = me.ui.$widget,
			$grade = $widget.find('.gradeStar');
		$grade.find('.gradeList li').removeClass('on');
		$grade.find('.js-submitGrade').removeAttr('disabled').removeClass('disabled');
		$grade.find('.gradeResult').html('');
		$('#gradeScore').val(0);
		$('#evaluation').val('');
	};

	Module.prototype.showTip = function (elm, msg, type) {
		var cls = 'ballonToolTip';
		if (type == 'warn') {
			msg = '<i class="warningTipIcon"></i>' + msg;
			cls += ' text-blue';
		}
		var d = dialog({
			skin: cls,
			content: msg,
			quickClose: true// 点击空白处快速关闭
		});
		d.show(elm);
	};

	/**
	 * 切换到其它客服
	 * @param obj
	 * obj eg: {joinId: 30}
	 */
	Module.prototype.switchToService = function (obj) {
		if (obj.joinId) {
			var me = this,
				joinId = obj.joinId;
			if (me.serviceId) { // 将之前的客服ID记录到列表当中
				if (!$.isPlainObject(me.serviceList)) {
					me.serviceList = {};
				}
				me.serviceList[joinId] = me.serviceId;
			}
			me.serviceId = joinId;
			me.showSysMessage('系统已经为您转接到"' + obj.fromJobNumber + '"');
		}
	};

	Module.prototype.getCallerStatusResponded = function (obj) {
		if (obj.joinId) {
			var me = this;
			obj.serviceId = me.serviceId;
			obj.status = 1;
			me.socket.emit('controller', obj);
		}
	};

	Module.prototype.offlineByService = function () {
		this.disconnectSocket();
	};

	return Module;
})();